#pragma once

#include "VariableManager.hpp"
#include <limits>

template <typename T>
class MathVariable : public RawVariable<T>
{
public:
	MathVariable(const string &name, uint id, uint flags = 0) : RawVariable<T>(name, id, flags) { }

	virtual void LoadFromString(IParamObject &obj, const string &s)
	{
		T v;
		MustStrToSth<T>(&v, s);
		SetValue(obj, v);
	}

	virtual void SaveToString(const IParamObject &obj, string &out)
	{
		T v;
		GetValue(obj, v);
		SthToStr<T>(&out, v);
	}
};

template <typename T>
class VecVariable : public MathVariable<T>
{
public:
	T MinValue, MaxValue; // T(), T() (both zeros) means no limits. Derived class must initialize these fields!!!
	float StepValue; // 0 means default.
	uint Precision; // 0 means default.
	bool IsDirection; // I don't state yet whether it means the vector has to be normalized.

	VecVariable(const string &name, uint id, uint flags = 0)
	: MathVariable<T>(name, id, flags)
	, StepValue(0.f)
	, Precision(0)
	, IsDirection(false)
	{
	}
};

class Vec2Variable : public VecVariable<VEC2>
{
public:
	Vec2Variable(const string &name, uint id, uint flags = 0) : VecVariable<VEC2>(name, id, flags) { MinValue = MaxValue = VEC2_ZERO; }
	virtual const char * GetTypeName() { return "vec2"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class Vec3Variable : public VecVariable<VEC3>
{
public:
	Vec3Variable(const string &name, uint id, uint flags = 0) : VecVariable<VEC3>(name, id, flags) { MinValue = MaxValue = VEC3_ZERO; }
	virtual const char * GetTypeName() { return "vec3"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class Vec4Variable : public VecVariable<VEC4>
{
public:
	Vec4Variable(const string &name, uint id, uint flags = 0) : VecVariable<VEC4>(name, id, flags) { MinValue = MaxValue = VEC4_ZERO; }
	virtual const char * GetTypeName() { return "vec4"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

template <typename T>
class BaseColorVariable : public MathVariable<T>
{
public:
	bool UseAlpha;

	BaseColorVariable(const string &name, uint id, uint flags = 0, bool useAlpha = false) : MathVariable<T>(name, id, flags), UseAlpha(useAlpha) { }
};

class ColorVariable : public BaseColorVariable<COLOR>
{
public:
	ColorVariable(const string &name, uint id, uint flags = 0, bool useAlpha = false) : BaseColorVariable<COLOR>(name, id, flags, useAlpha) { }
	virtual const char * GetTypeName() { return "color"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class ColorfVariable : public BaseColorVariable<COLORF>
{
public:
	ColorfVariable(const string &name, uint id, uint flags = 0, bool useAlpha = false) : BaseColorVariable<COLORF>(name, id, flags, useAlpha) { }
	virtual const char * GetTypeName() { return "colorf"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class QuaternionVariable : public MathVariable<QUATERNION>
{
public:
	QuaternionVariable(const string &name, uint id, uint flags = 0) : MathVariable<QUATERNION>(name, id, flags) { }
	virtual const char * GetTypeName() { return "quaternion"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class PointVariable : public MathVariable<POINT_>
{
public:
	POINT_ MinValue, MaxValue; // (0, 0), (0, 0) means no limits.
	PointVariable(const string &name, uint id, uint flags = 0) : MathVariable<POINT_>(name, id, flags), MinValue(0, 0), MaxValue(0, 0) { }
	virtual const char * GetTypeName() { return "point"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class RectVariable : public MathVariable<RECTI>
{
public:
	POINT_ MinValue, MaxValue; // (0, 0), (0, 0) means no limits.
	RectVariable(const string &name, uint id, uint flags = 0) : MathVariable<RECTI>(name, id, flags), MinValue(0, 0), MaxValue(0, 0) { }
	virtual const char * GetTypeName() { return "rect"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class RectfVariable : public MathVariable<RECTF>
{
public:
	RectfVariable(const string &name, uint id, uint flags = 0) : MathVariable<RECTF>(name, id, flags) { }
	virtual const char * GetTypeName() { return "rectf"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class BoxVariable : public MathVariable<BOX>
{
public:
	BoxVariable(const string &name, uint id, uint flags = 0) : MathVariable<BOX>(name, id, flags) { }
	virtual const char * GetTypeName() { return "box"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};

class MatrixVariable : public MathVariable<MATRIX>
{
public:
	enum
	{
		ENABLE_ROW_1 = 0x01, ENABLE_ROW_2 = 0x02, ENABLE_ROW_3 = 0x04, ENABLE_ROW_4 = 0x08,
		ENABLE_COL_1 = 0x10, ENABLE_COL_2 = 0x20, ENABLE_COL_3 = 0x40, ENABLE_COL_4 = 0x80,
		ENABLE_ALL_ROWS = 0x0F,
		ENABLE_ALL_COLS = 0xF0,
		ENABLE_ALL = 0xFF,
	};

	/// row, col = 0..3
	static inline bool IsCellEnabled(unsigned enableFlags, int row, int col)
	{
		return (enableFlags & (0x01 << row)) && (enableFlags & (0x10 << col));
	}

	uint EnableFlags;

	MatrixVariable(const string &name, uint id, uint flags = 0, uint enableFlags = ENABLE_ALL) : MathVariable<MATRIX>(name, id, flags), EnableFlags(enableFlags) { }
	virtual const char * GetTypeName() { return "matrix"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tok);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tw);
};
